package smartcase.errors;

import smartcase.base.func.Func;
import smartcase.base.func.ThrowingFunc;
import smartcase.base.func.ThrowingSupplier;

public class Errors<V> {

  private final V value;
  private RuntimeException error;
  private boolean throwException = false;

  private Errors(V value) {
    this.value = value;
  }

  public static Errors<Void> map(ThrowingFunc func) {
    return map(
        () -> {
          func.execute();
          return null;
        });
  }

  public static <T> Errors<T> map(ThrowingSupplier<T> supplier) {
    try {
      return new Errors<>(supplier.apply());
    } catch (Throwable e) {
      e.printStackTrace();
      Errors<Object> errors = new Errors<>(null);
      errors.error = new BaseException(e.getMessage());
      return (Errors<T>) errors;
    }
  }

  public HandFinally<V> orElseThrow() {
    if (error != null) {
      this.throwException = true;
    }
    return new HandFinally<>();
  }

  public HandFinally<V> orElseThrow(BaseException e) {
    if (error != null) {
      this.throwException = true;
      if (e == null) e = new BaseException(error.getMessage());
      e.setMeg(error.getMessage());
      throw e;
    }
    return new HandFinally<>();
  }

  public class HandFinally<F> {

    private HandFinally() {}

    public F handFinally() {
      return handFinally(() -> {});
    }

    public F handFinally(Func func) {
      try {
        func.execute();
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        if (throwException) throw (error == null ? new BaseException("Unknow error") : error);
      }
      return (F) value;
    }
  }
}
